"""Flood router for Dingo.

Copyright (C) Sarah Mount 2008.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
"""

from __future__ import absolute_import
del absolute_import
from .. import RoutingModel

import copy

DEBUG = True
#DEBUG = False

class IdentStamper(object):
    __instance = None       
    def __new__(cls, *args, **kargs): 
        """Create a new instance of this class, ensuring conformance
        to the singleton pattern.
	
	TODO: Find a new way of performing unique ident stamping in
	the presence of RPC.

        See U{http://en.wikipedia.org/wiki/Singleton_pattern#Python}
        """
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args, **kargs)
        return cls.__instance
    def __init__(self):
        self.ident = 0
    	return
    def getIdent(self):
	self.ident += 1
	return self.ident


class FloodRouter(RoutingModel.RoutingModel):
    """Flood routing.

    Keeps a hopCount variable in each packet which is
    not allowed to exceed a given maximum value (default 10).
    """
    def __init__(self, ttl=10):
        RoutingModel.RoutingModel.__init__(self)
        self.name = 'Flood router'
        self.ttl = ttl # Time to live measured in hops
	self.messages = [] # Messages already forwarded by this router
        return

    def send(self, arc_colour):
        if not self.send_q.empty():
            msg = self.send_q.get()
            if not 'hopCount' in msg.__dict__: # First time msg has been sent
		msg.hopCount = 0
		if DEBUG: print 'Original message created by sensor: %s' % msg.sender_id
		msg.dest, msg.receiver_id = msg.receiver_id, None
		msg.route = {}
		# Timestamp message with original sending time
            	msg.time_sent = self.clock() # TODO: use the time model.
		# Give message a unique identifier
		msg.ident = IdentStamper().getIdent()
            msg.hopCount = msg.hopCount + 1
            msg.route[msg.hopCount] = (self.sensor_id, self.clock())
            # Drop packet if TTL exceeded
            if msg.hopCount > self.ttl: return 
            # If this router has already broadcast the message, drop it
            if msg.ident in self.messages: return
            # Otherwise forward packet by broadcast
            self.radio.broadcast(msg, arc_colour)
            self.messages.append(msg.ident)
            if DEBUG: print 'Message forwarded by sensor %s' % self.sensor_id
            return
        return

    def recv(self):
        """Process received packets according to this algorithm:
    	 * If the packet is destined for this sensor, return it.
         * If the packet has exceeded the max hop count, drop it.
         * Else rebroadcast it.
        """
        if self.recv_q.empty(): 
		if DEBUG: print 'Receive queue empty at sensor %s' % self.sensor_id
		return
        
        packet = self.recv_q.get()

	if DEBUG: self.debugRoute(packet)

	# CASE 0: Packet is intended for this sensor
        if packet.dest == self.sensor_id:
            if DEBUG: print 'MESSAGE %i ARRIVED AT DESTINATION %s' % (packet.ident, self.sensor_id)
            return packet
	# CASE 1: TTL exceeded. Drop the packet.
        elif packet.hopCount > self.ttl: return
	# CASE 2: Packet is not intended for this sensor. Queue for resending
	else: self.send_q.put(copy.copy(packet))
        return

    def debugRoute(self, msg):
	"""Return string representing route taken by message."""
	r = msg.route.keys()
	r.sort()
	print
	print 'Message %i received by sensor %s with following routing table:' % (msg.ident, self.sensor_id)
	print '--------------------------------------------------------------'
	print 'Intended destination: %s' % msg.dest
	print 'Current hop count: %i' % msg.hopCount
	print '---------------'
	for hop in r:
		print 'Hop %i : sensor %s : time %d' % (hop, msg.route[hop][0], msg.route[hop][1])
	print '---------------'
	print
	return


floodRouter = FloodRouter()
